/**
 * @file ssid_manager.cc
 * @brief Wi-Fi SSID管理器实现文件
 * 
 * 实现SSID管理器的所有功能，包括NVS数据持久化、
 * 网络列表管理、默认网络设置等。
 * 
 * @author SiaZhang
 * @version 2.4.2
 */

#include "ssid_manager.h"

#include <algorithm>
#include <esp_log.h>
#include <nvs_flash.h>

#define TAG "SsidManager"           ///< 日志标签
#define NVS_NAMESPACE "wifi"        ///< NVS命名空间
#define MAX_WIFI_SSID_COUNT 10      ///< 最大WiFi网络数量

/**
 * @brief 构造函数
 * 
 * 创建SSID管理器实例并从NVS加载已保存的网络配置
 */
SsidManager::SsidManager() {
    LoadFromNvs();
}

/**
 * @brief 析构函数
 */
SsidManager::~SsidManager() {
}

/**
 * @brief 清空所有保存的Wi-Fi网络
 * 
 * 清空内存中的网络列表并更新NVS存储
 */
void SsidManager::Clear() {
    ssid_list_.clear();
    SaveToNvs();
}

/**
 * @brief 从NVS闪存加载Wi-Fi配置
 * 
 * 从NVS的"wifi"命名空间读取保存的SSID和密码配置。
 * 键名格式：ssid, ssid1, ssid2, ... ssid9
 *          password, password1, password2, ... password9
 */
void SsidManager::LoadFromNvs() {
    ssid_list_.clear();

    // 打开NVS命名空间进行只读访问
    nvs_handle_t nvs_handle;
    auto ret = nvs_open(NVS_NAMESPACE, NVS_READONLY, &nvs_handle);
    if (ret != ESP_OK) {
        // 命名空间不存在，直接返回
        ESP_LOGW(TAG, "NVS namespace %s doesn't exist", NVS_NAMESPACE);
        return;
    }
    
    // 循环读取最多10个Wi-Fi网络配置
    for (int i = 0; i < MAX_WIFI_SSID_COUNT; i++) {
        // 构造SSID键名：ssid, ssid1, ssid2, ...
        std::string ssid_key = "ssid";
        if (i > 0) {
            ssid_key += std::to_string(i);
        }
        
        // 构造密码键名：password, password1, password2, ...
        std::string password_key = "password";
        if (i > 0) {
            password_key += std::to_string(i);
        }
        
        // 读取SSID和密码
        char ssid[33];      // WiFi SSID最大长度32字节
        char password[65];  // WiFi密码最大长度64字节
        size_t length = sizeof(ssid);
        if (nvs_get_str(nvs_handle, ssid_key.c_str(), ssid, &length) != ESP_OK) {
            continue;  // 该索引没有SSID，跳过
        }
        length = sizeof(password);
        if (nvs_get_str(nvs_handle, password_key.c_str(), password, &length) != ESP_OK) {
            continue;  // 该索引没有密码，跳过
        }
        
        // 添加到网络列表
        ssid_list_.push_back({ssid, password});
    }
    nvs_close(nvs_handle);
}

/**
 * @brief 保存Wi-Fi配置到NVS闪存
 * 
 * 将当前内存中的SSID列表保存到NVS的"wifi"命名空间。
 * 同时清理超出范围的旧数据。
 */
void SsidManager::SaveToNvs() {
    nvs_handle_t nvs_handle;
    ESP_ERROR_CHECK(nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs_handle));
    
    // 保存或清理所有可能的索引位置
    for (int i = 0; i < MAX_WIFI_SSID_COUNT; i++) {
        // 构造键名
        std::string ssid_key = "ssid";
        if (i > 0) {
            ssid_key += std::to_string(i);
        }
        std::string password_key = "password";
        if (i > 0) {
            password_key += std::to_string(i);
        }
        
        if (i < ssid_list_.size()) {
            // 保存当前索引的网络配置
            nvs_set_str(nvs_handle, ssid_key.c_str(), ssid_list_[i].ssid.c_str());
            nvs_set_str(nvs_handle, password_key.c_str(), ssid_list_[i].password.c_str());
        } else {
            // 清理超出范围的旧数据
            nvs_erase_key(nvs_handle, ssid_key.c_str());
            nvs_erase_key(nvs_handle, password_key.c_str());
        }
    }
    
    // 提交更改并关闭句柄
    nvs_commit(nvs_handle);
    nvs_close(nvs_handle);
}

/**
 * @brief 添加新的Wi-Fi网络
 * 
 * @param ssid Wi-Fi网络名称
 * @param password Wi-Fi网络密码
 * 
 * 添加逻辑：
 * 1. 检查SSID是否已存在，如果存在则更新密码
 * 2. 如果列表已满（10个），删除最后一个
 * 3. 将新网络添加到列表最前面（最高优先级）
 * 4. 保存到NVS
 */
void SsidManager::AddSsid(const std::string& ssid, const std::string& password) {
    // 检查SSID是否已存在
    for (auto& item : ssid_list_) {
        ESP_LOGI(TAG, "compare [%s:%d] [%s:%d]", item.ssid.c_str(), item.ssid.size(), ssid.c_str(), ssid.size());
        if (item.ssid == ssid) {
            ESP_LOGW(TAG, "SSID %s already exists, overwrite it", ssid.c_str());
            item.password = password;  // 更新密码
            SaveToNvs();
            return;
        }
    }

    // 如果列表已满，删除最后一个（优先级最低的）
    if (ssid_list_.size() >= MAX_WIFI_SSID_COUNT) {
        ESP_LOGW(TAG, "SSID list is full, pop one");
        ssid_list_.pop_back();
    }
    
    // 将新SSID添加到列表前端（最高优先级）
    ssid_list_.insert(ssid_list_.begin(), {ssid, password});
    SaveToNvs();
}

/**
 * @brief 根据索引删除Wi-Fi网络
 * 
 * @param index 要删除的网络索引（0-9）
 */
void SsidManager::RemoveSsid(int index) {
    // 检查索引有效性
    if (index < 0 || index >= ssid_list_.size()) {
        ESP_LOGW(TAG, "Invalid index %d", index);
        return;
    }
    
    // 删除指定索引的网络并保存
    ssid_list_.erase(ssid_list_.begin() + index);
    SaveToNvs();
}

/**
 * @brief 设置默认Wi-Fi网络
 * 
 * @param index 要设置为默认的网络索引
 * 
 * 将指定索引的网络移动到列表最前面，提高其连接优先级。
 * 这样在自动连接时会优先尝试连接该网络。
 */
void SsidManager::SetDefaultSsid(int index) {
    // 检查索引有效性
    if (index < 0 || index >= ssid_list_.size()) {
        ESP_LOGW(TAG, "Invalid index %d", index);
        return;
    }
    
    // 将指定网络移动到列表前端
    auto item = ssid_list_[index];                          // 复制目标项
    ssid_list_.erase(ssid_list_.begin() + index);          // 删除原位置
    ssid_list_.insert(ssid_list_.begin(), item);           // 插入到前端
    SaveToNvs();
}
